package com.goodcol.plugin.entity;

import com.goodcol.plugin.dto.GenerateOptions;
import com.goodcol.plugin.tool.CompareFileUtils;
import com.goodcol.plugin.tool.FileUtils;
import com.goodcol.plugin.tool.MessageDialogUtils;
import com.goodcol.plugin.tool.ProjectUtils;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.testFramework.LightVirtualFile;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;

import java.io.File;

/**
 * 需要保存的文件
 * <p>
 * 如果文件保存在项目路径下，则使用idea提供的psi对象操作。如果文件保存在非项目路径下，则使用java原始IO流操作。
 *
 * @author QuickCode
 * @version 1.0.0
 */
@Data
public class SaveFile {
    private static final Logger LOG = Logger.getInstance(SaveFile.class);
    /**
     * 所属项目
     */
    private Project project;
    /**
     * 文件内容
     */
    private String content;
    /**
     * 文件工具类
     */
    private FileUtils fileUtils = FileUtils.getInstance();
    /**
     * 回调对象
     */
    private Callback callback;
    /**
     * 生成配置
     */
    private GenerateOptions generateOptions;

    /**
     * 最终生成的文件的目录对象VirtualFile
     */
    @Getter
    @Setter
    private VirtualFile directory;

    /**
     * 最终生成的文件对象VirtualFile
     */
    @Getter
    @Setter
    private VirtualFile file;

    /**
     * 保存文件
     *
     * @param project         项目
     * @param content         内容
     * @param callback        回调
     * @param generateOptions 生成选项
     */
    public SaveFile(@NonNull Project project, @NonNull String content, @NonNull Callback callback, @NonNull GenerateOptions generateOptions) {
        this.project = project;
        this.callback = callback;
        this.content = content.replace("\r", "");
        this.generateOptions = generateOptions;
    }

    /**
     * 当前文件转换处理器
     *
     * @param action 动作 1=覆盖；2=比较
     */
    public void convertFileHandler(int action) {
        if (action == 1) {
            // 覆盖文件
            Document document = coverFile(file);
            // 执行代码格式化操作
            if (Boolean.TRUE.equals(callback.getReformat())) {
                FileUtils.getInstance().reformatFile(project, file);
            }
            // 提交文档改动，并非VCS中的提交文件
            if (document != null) {
                PsiDocumentManager.getInstance(project).commitDocument(document);
            }
        } else if (action == 2) {
            // 对比代码时也格式化代码
            String newText = content;
            if (Boolean.TRUE.equals(callback.getReformat())) {
                // 1.先读取旧文件内容
                // 2.将新的文件内容写入到旧文件中，然后对这个文件进行格式化（利用原文件的位置进行格式化操作），获得格式化后的新文件内容
                // 3.最后还原旧文件内容
                String oldText = getFileText(file);
                Document tmpDoc = coverFile(file);
                // 格式化代码
                FileUtils.getInstance().reformatFile(project, file);
                // 提交文档改动，并非VCS中的提交文件
                PsiDocumentManager.getInstance(project).commitDocument(tmpDoc);
                // 获取新的文件内容
                newText = getFileText(file);
                // 还原旧文件
                coverFile(file, oldText);
            }
            FileType fileType = FileTypeManager.getInstance().getFileTypeByFileName(callback.getFileName());
            CompareFileUtils.showCompareWindow(project, file, new LightVirtualFile(callback.getFileName(), fileType, newText));
        }
    }

    /**
     * 文件是否为项目文件
     *
     * @return 是否为项目文件
     */
    private boolean isProjectFile() {
        VirtualFile baseDir = ProjectUtils.getBaseDir(project);
        // 无法获取到项目基本目录，可能是Default项目，直接返回非项目文件
        if (baseDir == null) {
            return false;
        }
        // 路径对比，判断项目路径是否为文件保存路径的子路径
        String projectPath = handlerPath(baseDir.getPath());
        String tmpFilePath = handlerPath(callback.getSavePath());
        if (tmpFilePath.length() > projectPath.length()
                && (!"/".equals(tmpFilePath.substring(projectPath.length(), projectPath.length() + 1)))) {
            return false;
        }
        return tmpFilePath.indexOf(projectPath) == 0;
    }

    /**
     * 处理路径，统一分割符并转小写
     *
     * @param path 路径
     * @return 处理后的路径
     */
    private String handlerPath(String path) {
        return handlerPath(path, true);
    }

    /**
     * 处理路径，统一分割符并转小写
     *
     * @param path      路径
     * @param lowerCase 是否转小写
     * @return 处理后的路径
     */
    private String handlerPath(String path, boolean lowerCase) {
        // 统一分割符
        path = path.replace("\\", "/");
        // 避免重复分割符
        path = path.replace("//", "/");
        // 统一小写
        return lowerCase ? path.toLowerCase() : path;
    }

    /**
     * 创建目录
     *
     * @return 返回目录
     */
    public VirtualFile createFileDir() {
        if (!Boolean.TRUE.equals(callback.getWriteFile())) {
            return null;
        }
        // 判断目录是否存在
        VirtualFile baseDir = ProjectUtils.getBaseDir(project);
        if (baseDir == null) {
            throw new IllegalStateException("项目基本路径不存在");
        }
        // 处理保存路径
        String savePath = handlerPath(callback.getSavePath(), false);
        if (isProjectFile()) {
            // 删除保存路径的前面部分
            savePath = savePath.substring(handlerPath(baseDir.getPath()).length());
        } else {
            baseDir = null;
        }
        // 删除开头与结尾的/符号
        while (savePath.startsWith("/")) {
            savePath = savePath.substring(1);
        }
        while (savePath.endsWith("/")) {
            savePath = savePath.substring(0, savePath.length() - 1);
        }
        // 查找保存目录是否存在
        VirtualFile saveDir;
        if (baseDir == null) {
            saveDir = VfsUtil.findFileByIoFile(new File(savePath), false);
        } else {
            saveDir = VfsUtil.findRelativeFile(baseDir, savePath.split("/"));
        }
        // 提示创建目录
        VirtualFile dir = titleCreateDir(saveDir, baseDir, savePath, true);
        this.directory = dir;
        return dir;
    }

    /**
     * 提示创建目录
     *
     * @param saveDir      保存路径
     * @param baseDir      baseDir
     * @param savePath     savePath
     * @param ignorePrompt 是否忽略是否创建目录提示的Dialog，为true时不弹出提示默认创建不存在的目录
     * @return 是否放弃执行
     */
    private VirtualFile titleCreateDir(VirtualFile saveDir, VirtualFile baseDir, String savePath, boolean ignorePrompt) {
        if (saveDir != null) {
            return saveDir;
        }
        // 尝试创建目录
        String msg = String.format("Directory %s Not Found, Confirm Create?", callback.getSavePath());
        if (Boolean.TRUE.equals(generateOptions.getTitleSure())) {
            saveDir = fileUtils.createChildDirectory(project, baseDir, savePath);
            return saveDir;
        } else if (Boolean.TRUE.equals(generateOptions.getTitleRefuse())) {
            return null;
        } else {
            if (ignorePrompt || MessageDialogUtils.yesNo(msg)) {
                saveDir = fileUtils.createChildDirectory(project, baseDir, savePath);
                return saveDir;
            }
        }
        return null;
    }

    /**
     * 保存或替换文件
     *
     * @return 文件处理结果状态。
     * 0=创建文件失败；
     * 1=生成一个新的文件成功；
     * 2=存在的文件已被覆盖；
     * 3=不覆盖文件，即跳过已经存在的文件；
     * 4=需要人工选择进一步确认对文件的处理方式；
     */
    public int saveOrReplaceFile() {
        int fileFlag;
        Document document = null;
        // 文件不存在直接创建
        if (file == null) {
            file = fileUtils.createChildFile(project, directory, callback.getFileName());
            if (file == null) {
                fileFlag = 0;//创建文件失败
            } else {
                fileFlag = 1; //生成一个新的文件成功
                document = coverFile(file);
            }
        } else {
            if (Boolean.TRUE.equals(generateOptions.getTitleSure())) { //覆盖文件
                fileFlag = 2; //存在的文件已被覆盖
                document = coverFile(file);
            } else if (Boolean.TRUE.equals(generateOptions.getTitleRefuse())) { //不覆盖文件
                fileFlag = 3;// 不覆盖文件，即跳过已经存在的文件
            } else {
                fileFlag = 4;// 需要人工选择进一步确认对文件的处理方式
            }
        }
        if (document != null) {
            // 执行代码格式化操作
            if (Boolean.TRUE.equals(callback.getReformat())) {
                FileUtils.getInstance().reformatFile(project, file);
            }
            // 提交文档改动，并非VCS中的提交文件
            PsiDocumentManager.getInstance(project).commitDocument(document);
        }
        return fileFlag;
    }

    private String getFileText(VirtualFile file) {
        FileDocumentManager fileDocumentManager = FileDocumentManager.getInstance();
        Document document = fileDocumentManager.getDocument(file);
        if (document == null) {
            throw new IllegalStateException("virtual file to document failure");
        }
        return document.getText();
    }

    /**
     * 覆盖文件
     *
     * @param file 文件
     * @return 覆盖后的文档对象
     */
    private Document coverFile(VirtualFile file) {
        return coverFile(file, content);
    }

    /**
     * 覆盖文件
     *
     * @param file 文件
     * @param text 文件内容
     * @return 覆盖后的文档对象
     */
    private Document coverFile(VirtualFile file, String text) {
        return FileUtils.getInstance().writeFileContent(project, file, callback.getFileName(), text);
    }
}
